﻿using Microsoft.Cci;
using Microsoft.Cci.MutableCodeModel;
using System;
using System.Collections.Generic;
using System.Linq;

namespace AutoPex.Core.Extensions
{
	internal static class IMethodDefinitionExtensions
	{
		private static IMethodDefinition GetMethod(object operationValue)
		{
			var method = operationValue as IMethodDefinition;
			
			if(method == null)
			{
				method = (operationValue as IMethodReference).ResolvedMethod;
			}	
			
			return method;
		}
		
		internal static bool IsDifferentFrom(this IMethodDefinition @this, IMethodDefinition target)
		{
			var visitedMethods = new List<IMethodDefinition>() { @this };
			return @this.IsDifferentFrom(target, visitedMethods);
		}

		private static bool IsDifferentFrom(this IMethodDefinition @this, IMethodDefinition target, List<IMethodDefinition> visitedMethods)
		{
			bool isDifferentFrom = false;
			
			var thisOperations = @this.Body.Operations.ToList();
			var targetOperations = target.Body.Operations.ToList();

			if(thisOperations.Count != targetOperations.Count)
			{
				isDifferentFrom = true;
			}
			else
			{
				for(var i = 0; i < thisOperations.Count; i++)
				{
					var thisOperation = thisOperations[i];
					var targetOperation = targetOperations[i];
					
					if(thisOperation.OperationCode != targetOperation.OperationCode)
					{
						isDifferentFrom = true;
						break;
					}
					else if(thisOperation.OperationCode.IsMethodCall())
					{
						var thisMethod = IMethodDefinitionExtensions.GetMethod(thisOperation.Value);
						var targetMethod = IMethodDefinitionExtensions.GetMethod(targetOperation.Value);
						
						if(thisMethod.Name.Value == targetMethod.Name.Value)
						{
							if(!visitedMethods.Contains(thisMethod))
							{
								visitedMethods.Add(thisMethod);

								if(thisMethod.IsDifferentFrom(targetMethod, visitedMethods))
								{
									isDifferentFrom = true;
									break;
								}
							}
						}
						else
						{
							isDifferentFrom = true;
							break;						
						}
					}
				}
			}
			
			return isDifferentFrom;
		}
	}
}
